#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <getopt.h>
#include <sys/wait.h>

/* Player setup */
#define PLAYER_PROG "/usr/bin/mplayer"
#define SOUND_ONEREP "./gong.wav"
#define SOUND_RECOVER "./gong.wav"
#define SOUND_COUNTDOWN "./gong.wav"
#define SOUND_BEEP "./beep.wav"

/* shell colors */
#define RESET_COLOR "\033[m"
#define SET_COLOR "\033[0;"
#define GREEN 32
#define CYAN 36
#define RED 31

struct train {
	int max_reps;
	/* those two are in seconds */
	int time_one_rep;
	int time_recover;
	int countdown;
};

struct gtimer {
	int hour;
	int min;
	int sec;
};

struct gtimer gt;

void print_time(int t, int color)
{
	gt.sec = t % 60;
	gt.hour = t / 3600;
	gt.min = (t - gt.hour * 3600) / 60;

	printf("%s%dm%02d:%02d:%02d\r", SET_COLOR, color, gt.hour, gt.min, gt.sec);
}

void print_countdown(int t, int color)
{
	int i = t;
	while (i >= 0) {
		print_time(i, color);
		sleep(1);
		printf("\r");
		fflush(stdout);
		i--;
	}
	printf("\n");

}

void do_one_rep(struct train *t)
{
	print_countdown(t->time_one_rep, GREEN);
}

void do_recover(struct train *t)
{
	print_countdown(t->time_recover, CYAN);
}

void do_play_sound(const char *song)
{
	int len = strlen(song) + strlen(PLAYER_PROG) + 20 ;
	char *cmdline = calloc(len, sizeof(char));
	if (cmdline == NULL) {
		fprintf(stderr, "No memory available.\n");
		return;
	}
	sprintf(cmdline, "%s %s >/dev/null 2>&1", PLAYER_PROG, song);
	system(cmdline);
	free(cmdline);
}

/* let's play the song file in background */
void play_sound(const char *song)
{
	pid_t pid = fork();
	if (pid == 0) {
		do_play_sound(song);
		_exit(0);
	}
}

void do_countdown(struct train *t)
{
	int i = t->countdown;
	while (i > 0) {
		play_sound(SOUND_BEEP);
		printf("%s%dm%d", SET_COLOR, RED, i);
		sleep(1);
		printf(".");
		fflush(stdout);
		i--;
	}
	printf("\n");
}

void do_training(struct train *t)
{
	int i = 1;
	while (i <= t->max_reps) {
		printf("%sRound %d\n", RESET_COLOR, i);
		do_countdown(t);
		play_sound(SOUND_COUNTDOWN);
		do_one_rep(t);
		play_sound(SOUND_ONEREP);
		do_recover(t);
		play_sound(SOUND_RECOVER);
		i++;
	}
}

void pause_child(pid_t pid, int in_pause)
{
	int signum = (in_pause == 0) ? SIGSTOP : SIGCONT;
	kill(pid, signum);
}

int getch(void) {
	struct termios oldt, newt;
	int ch;
	/* We can't necessarily trust POSIX's STDIN_FILENO */
	int stdin_fd = fileno(stdin);

	/* Get and save the current terminal flags */
	tcgetattr(stdin_fd, &oldt);
	/* Copy the old settings and turn off echo/canonical mode */
	newt = oldt;
	newt.c_lflag &= ~(ICANON | ECHO);
	/* Apply the new settings */
	tcsetattr(stdin_fd, TCSANOW, &newt);
	/* Call the standard getchar() function */
	ch = getchar();
	/* Restore the original settings */
	tcsetattr(stdin_fd, TCSANOW, &oldt);
	return ch;
}

/* check player and audio file exists */
int exists_file(const char *file)
{
	int fd = open(file, O_RDONLY);
	if (fd == -1) {
		fprintf(stderr, "Error '%s' not found.\n", file);
		return 0;
	}
	close(fd);
	return 1;
}

int player_check_exists()
{
	if (exists_file(PLAYER_PROG) == 0)
		return 1;
	if (exists_file(SOUND_ONEREP) == 0)
		return 1;
	if (exists_file(SOUND_RECOVER) == 0)
		return 1;
	if (exists_file(SOUND_COUNTDOWN) == 0)
		return 1;
	if (exists_file(SOUND_BEEP) == 0)
		return 1;
	return 0;
}

static struct option long_options[] = {
	{"reps", required_argument, 0, 'm'},
	{"time", required_argument, 0, 'o'},
	{"recover", required_argument, 0, 'r'},
	{"countdown", optional_argument, 0, 'c'},
	{NULL, 0, 0, 0}
};

void usage(const char *program_name)
{
	fprintf(stderr,
		"Usage: %s -m <number> -o <seconds> -r <seconds> "
		"[-c <seconds>]\n", program_name);
	fprintf(stderr,
		"   --reps      or -m   number of sessions\n"
		"   --time      or -o   seconds per session\n"
		"   --recover   or -r   seconds to recover\n"
		"   --countdown or -c   seconds of countdown\n");
}

void parse_args(int argc, char *argv[], struct train *t)
{
	int c;
	int mandatory = 0;
	/* vedere di fare la conversione da minuti o qualsiasi altra cosa
	 * a secondi */
	while (1) {
		int option_index = 0;
		c = getopt_long(argc, argv, "m:o:r:c:",
				long_options, &option_index);
		if (c == -1)
			break;

		switch(c) {
		case 'm':
			t->max_reps = atoi(optarg);
			mandatory |= 1;
			break;
		case 'o':
			t->time_one_rep = atoi(optarg);
			mandatory |= 2;
			break;
		case 'r':
			t->time_recover = atoi(optarg);
			mandatory |= 4;
			break;
		case 'c':
			t->countdown = atoi(optarg);
			break;
		default:
			usage(argv[0]);
			_exit(1);
		}
	}

	if (mandatory != 7) {
		usage(argv[0]);
		_exit(1);
	}
}

int main(int argc, char *argv[])
{
	pid_t pid;
	struct train t;

	memset(&t, 0, sizeof(t));
	memset(&gt, 0, sizeof(gt));

	if (player_check_exists() == 1)
		return 1;

	/* default value 3 */
	t.countdown = 3;

	parse_args(argc, argv, &t);

	pid = fork();
	if (pid == 0) {
		pid_t ppid = getppid();
		do_training(&t);
		kill(ppid, SIGINT);
		_exit(0);
	} else {
		int pause = 0;
		while (1) {
			char c = getch();
			switch (c) {
			case ' ': case '\n':
				pause_child(pid, pause);
				pause = !pause;
				break;
			case 'q': case 'Q':
				kill(pid, SIGINT);
				goto exit_program;
			}
		}
exit_program:
		_exit(0);
	}
	return 0;
}
